import 'todo.dart';
import 'package:meta/meta.dart';

/// 类与对象
// ******************************** 类 ********************************
// 类是包含特定功能的代码段。通过 class 关键字声明，类中包含两⼤内容
// · 属性（变量|常量在类中称为属性）
// · ⽅法（函数在类中称为⽅法）
class Person {
  String name = "Jack";
  int age = 1;

  say() {
    print("hello ${name}, age ${age}");
  }
}

// 继承
// 类可以根据先后关系，分为 ⽗类 和 ⼦类。⼀般在⽗类中声明公共的内容，
// 在⼦类中写具体的实现，⼦类 通过继承的⽅式，获取⽗类中声明的公共的内容（属性和⽅法）
// 类的继承通过 extends 关键字实现。⼦类中调⽤⽗类中的⽅法，通过 super 关键字来实现
class Child extends Person {
  String name = "Tom";
  String sex = "boy";
  say() {
    print("hello ${name}, sex ${sex}");
  }
}
// ******************************** 对象 ********************************

// ******************************** 构造函数 ********************************
/// 默认构造器
// 默认构造器是⼀个与类同名的⽅法，在实例化时，⾃动被调⽤。
// 如果没有声明构造器，会提供⼀个默认的构造器。默认构造器是⽆参的，并且会调⽤⽗类的⽆参构造器
// ⼦类不会从⽗类继承构造器。⼦类如果不声明构造器的话，那么就只会有默认的⽆参构造器。
class Point {
  num x, y;

  // 构造器是⼀个与类名同名的⽅法
  Point(num x, num y) {
    this.x = x;
    this.y = y;
  }
  // 构造函数可缩略
  // Point(x, this.y);
}

// 命名构造器
class Point2 {
  num x, y;

  // 构造函数可缩略
  Point2(this.x, this.y);

  Point2.region() {
    this.x = 0;
    this.y = 0;
  }
}

/// 常量构造器
// 如果⽣成类的对象不会变，可以定义常量构造函数。常量构造函数可以创建⼀个规范化的实例。
// 声明为常量构造函数后，初始化对象时，避免重复开辟新的内存空间。
// 普通的实例化操作，每次实例化对象时，都需要在内存中开辟⼀块新的空间。例如：
void funcion1() {
  var p1 = new Point2(1, 2);
  var p2 = new Point2(1, 2);
  // 返回 false。因为每次实例化，都会重新开辟内存空间
  print(p1 == p2);
}

// 关于常量构造函数，有以下⼏个特点：
// 1. 常量构造函数必须⽤ const 关键词修饰
// 2. 常量构造函数必须⽤于成员变量都是 final 的类
// 3. final 属性不设置初始值
// 4. 常量构造函数不允许有函数体
// 5. 构建常量实例必须使⽤定义的常量构造函数
// 6. 实例化时需要加 const 关键字，否则实例化的对象仍然可以修改变量值
class ImmutablePoint {
  final num x;
  final num y;
  const ImmutablePoint(this.x, this.y);
}

void function2() {
  // 使⽤构造器创建两个相等的编译时常量，它们会⽣成同⼀个实例
  var p3 = const ImmutablePoint(1, 2);
  var p4 = const ImmutablePoint(1, 2);
  print(p3 == p4);
  // 返回 true。因为第⼀次开辟内存空间后，再次通过常量构造函数创建对象
  // 如果对象的属性完全⼀样，Dart 会指向相同的内存空间。⽽不是再次开辟新的内存空间

  // 内部的const可以省略
  const pointAndLine = const {
    'point': [ImmutablePoint(1, 2)]
  };
}

/// ⼯⼚构造函数
// 执⾏构造函数并不总是创建这个类的⼀个新实例时。可以考虑使⽤⼯⼚构造函数。
// ⼯⼚函数不会⾃动⽣ 成实例（因此，⼯⼚构造函数中，不能使⽤ this 关键字），
// ⽽是通过代码来决定返回的实例。
class SunGuoPerson {
  String name;

  static SunGuoPerson _sunGuoPerson;

  factory SunGuoPerson([String name = "刘备"]) {
    if (SunGuoPerson._sunGuoPerson == null) {
      _sunGuoPerson = new SunGuoPerson.newSelf(name);
    }
    return SunGuoPerson._sunGuoPerson;
  }

  SunGuoPerson.newSelf(this.name);
}

void function3() {
  SunGuoPerson p1 = new SunGuoPerson("关羽");
  print(p1.name);
  SunGuoPerson p2 = new SunGuoPerson('张⻜');
  print(p2.name);
  print(p1 == p2);
}

// ******************************** 访问修饰符 ********************************
// Dart 没有类似于 Java 那样的 public, protected 和 private 成员访问限定符。
/// 私有
// 如果⼀个标识符以下划线 (_) 开头，则表示该标识符是私有的（模块内私有）
// 以下划线 (_) 开头的属性和⽅法，只在当前库内私有，即跨库私有。
// 私有属性 (以下划线开头, 只能在当前类中访问)
// 私有⽅法(以下划线开头, 只能在当前类中访问)
/// static
// 使⽤ static 关键字实现类范围的变量和⽅法
// 静态属性
//     通过 static 关键字声明的属性是静态属性
//     静态属性可以通过类名称直接访问，即⽆需实例化过程（类名.属性名）
// 静态变量只有被使⽤的时候才会初始化（不使⽤，不初始化，不占空间）
//     静态⽅法通过 static 关键字声明的⽅法是静态⽅法 静态⽅法可以通过类名称直接访问，即⽆需实例化过程（类名.⽅法名())
//     静态⽅法（类⽅法）不能在实例上使⽤，因此，不能通过 this 访问静态⽅法 静态⽅法不能访问⾮静态成员，⾮静态⽅法可以访问静态成员

// ******************************** getter 与 setter ********************************
/// getter
// getter 声明的函数通过 get 关键字修饰，函数没有⼩括号，访问时也没有⼩括号（像访问属性⼀样访问⽅法）
/// setter
// setter 声明的函数通过 set 关键字声明，访问时，像设置属性⼀样给函数传参
class Rect {
  num height;
  num width;

  Rect(this.height, this.width);

  // 普通方法的声明
  num area() {
    return this.height * this.width;
  }

  // getter 方法的声明（类型关键字放在get前面）
  num get areaGet {
    return this.height * this.width;
  }

  set myHight(value) {
    this.height = value;
  }
}

// ******************************** 元数据（注解） ********************************
// 使⽤元数据可以为代码增加⼀些额外的信息。元数据注解以 @ 开头，其后紧跟⼀个编译时常量（⽐如 deprecated ）或者调⽤⼀个常量构造函数。
// 元数据可以在 library、class、typedef、type parameter、 constructor、factory、function、field、 parameter 或者 variable 声明之前使⽤，
// 也可以在 import 或 export 之前使⽤。可使⽤反射在运⾏时获 取元数据信息。
/// @override
// 当⽗类和⼦类中出现同名⽅法后，⼦类中的⽅法可以覆盖⽗类中的同名⽅法。此时，我们可以在⼦类的同名⽅法前加上注解 @override，⽤来标记当前⽅法是⼀个覆盖的⽅法。
class Teacher {
  void work() {
    print("我是老师");
  }
}

class Student extends Teacher {
  @override
  void work() {
    print("我是学生");
  }
}

/// @required
// 如果某参数是必填参数，则可以通过 @required 来修饰
// {} ?????
void function({int a, @required int b}) {}

/// @deprecated
// 若某类或某⽅法加上该注解之后，表示此⽅法或类不再建议使⽤。
class Television {
  @deprecated // 表明该⽅法即将被废弃
  void activate() {
    turnOn();
  }

  void turnOn() {
    print("Television turnOn");
  }
}

/// 自定义元数据
@Todo("Json", "make this do something")
void doSomething() {
  print('do Something');
}

// ******************************** 抽象类 ********************************
// 抽象类可以看作类的模板：
//     ⽤来约定⼦类中，必须出现的属性或⽅法。
// 抽象类有如下特点：
// · 抽象类是通过 abstract 关键字来定义的类
// · 抽象类不能被实例化
// · 抽象⽅法是指不含⽅法体的⽅法，Dart 中，抽象⽅法不能通过 abstract 关键字来声明
// · 抽象类中可以有抽象⽅法，也可以没有抽象⽅法（⼀般都会有抽象⽅法）
// · 如果⼦类继承抽象类，⼦类必须得实现（覆写）抽象类中所有的抽象⽅法
//  · 如果把抽象类当做接⼝实现的话，必须得实现抽象类⾥⾯定义的所有属性和⽅法
abstract class phone {
  void chip();
  void camera();

  void name() {
    print("我是一个抽象类里面的普通方法。");
  }
}

class xiaomi extends phone {
  @override
  void chip() {
    print("骁龙888");
  }

  @override
  void camera() {
    print("三星摄像头");
  }
}
// 多态：多态就是⽗类定义⼀个⽅法不去实现，让继承他的⼦类去实现，每个⼦类有不同的表现。

// ******************************** 接口 ********************************
// 类和接口是统一的，类就是接口, 因为接口中约定的内容，必须要在类中实现，所以一般在Dart中，
// 使用抽象类作为接口
/**
 * 处理器
 */
abstract class Processor {
  String cores; // 内存核数: 2核
  arch(String name); // 芯片工艺: 5nm
}

class Phone extends Processor {
  @override
  String cores;

  @override
  arch(String name) {
    print('芯片工艺: ' + name);
  }
}

// ******************************** 混入 ********************************
// Mixin 是⼀段公共代码
// Mixin （混⼊）可以看作类的公共代码。⼀般，我们可以将⼀些公共的属性和⽅法，放在在 Mixin 中。然后类通过引⼊ Mixin。
// 实现多个类复⽤⼀段公共代码（Mixin 是⼀种在单继承环境中，重⽤代码的⽅法）
/// 把类⽤作 Mixin
class ClassName {
  String name = "Jack";

  void run() {
    print("My Name is ClassName");
  }
}

/// 单独声明混⼊（Mixin）
mixin ClassName2 {
  String name = "Jack2";

  void run() {
    print("My Name is ClassName2");
  }
}

/// 通过 with 关键字引⼊ Mixin
// 类可以通过 with 关键字引⼊ Mixin，⼀个类可以使⽤⼀个 Mixin，也可以使⽤多个 Mixin
class ClassName3 with ClassName, ClassName2 {}

class ClassName4 extends ClassName with ClassName2 {}

// 继承与混⼊的区别?
// 继承是亲爹（Dart ⽀持单继承），只能有⼀个；混⼊（Mixin）是⼲爹，可以引⼊多个。
// 接⼝与混⼊的区别?
// 接⼝强制其⼦类实现其属性和⽅法，相当于是⼦类的模板；混⼊只提供公共内容，不强制使⽤。

// ******************************** 泛型 ********************************
// 泛型是⽤来解决类，接⼝，函数中，关于数据类型的校验问题。通常情况下，使⽤⼀个字⺟来代表类型参 数， 例如 E, T, S, K, 和 V 等。
// 泛型函数
// 返回类型 函数名<输⼊类型>（参数类型 参数） { // ... }
class ClassNameT {
  T getData<T>(T value) {
    return value;
  }

  setData<T>(T value) {
    print(value);
  }
}

void function4() {
  ClassNameT classNameT = new ClassNameT();
  print(classNameT.getData<int>(12));
  classNameT.setData<int>(12);
}

// 泛型类
class GenericsClass<T> {
  List list = <T>[];

  void add(T value) {
    this.list.add(value);
  }

  void printInfo() {
    for (var i = 0; i < this.list.length; i++) {
      print(this.list[i]);
    }
  }
}

void function5() {
  GenericsClass genericsClass = new GenericsClass<int>();
  // ????????
  genericsClass.add("ssss");
  genericsClass.add(23);
  genericsClass.printInfo();
}

// 泛型接⼝
// 泛型接⼝是将泛型⽤在接⼝中，使得接⼝中的数据类型可变。
abstract class Cache<T> {
  getByKey(String key);
  void setByKey(String key, T value);
}

class FlieCache<T> implements Cache<T> {
  @override
  getByKey(String key) {
    return null;
  }

  @override
  void setByKey(String key, T value) {
    print("⽂件缓存: key=${key} value=${value}");
  }
}

void function6() {
  FlieCache flieCache = new FlieCache<Map>();
  flieCache.setByKey('index', {"name": "张三丰", "age": 148});

  FlieCache flieCache2 = new FlieCache<String>();
  flieCache2.setByKey('hello', 'world');
}

// 限制泛型类型
// 使用泛型类型的时候，可以使用extends实现参数类型的限制
class FooBaseClass {}

class Foo<T extends FooBaseClass> {}

// 枚举
// 枚举类型也成为enumerations 或 enums，是一种特殊的类，用于表示数量固定的常量值。
// 使用enum关键字定义一个枚举类型
enum Color { red, green, blue }
// 枚举中的每个值都有一个index getter方法，该方法返回值所在枚举类型定义中的位置(从0开始)
void enumFunction() {
  print(Color.red.index);
  List<Color> lsColor = Color.values;
  print(lsColor);
}

// 枚举类型具有以下限制
// 枚举不能被⼦类化，混⼊或实现。
// 枚举不能被显式实例化。

void main(List<String> args) {
  function6();
}
